#include "crossbit/ExecutionEngine.h"
#include "crossbit/Exit.h"
#include "crossbit/Profiler.h"
#include "vector"

using namespace crossbit;

//#define DEBUG
//#define LINK
#define RBDEBUG
/*
void* mymyprint(char *str, int n)
{
	printf("In mymyprint: %s %d\n", str, n);
	return 0;
}

void* myMalloc(int size)
{
	printf("In myMalloc: size %d\n", size);
	return malloc(size);
}

void myFree(void *ptr)
{
	printf("In myFree: ptr %x\n", ptr);
	free(ptr);
}
*/

extern RBFuncIndx RBFuncTable[RBFUNCNUM];

void ExecutionEngine::run( XTMemAddr addr )
{
		XTInt32 count = 0;
		XTMemAddr entry = addr;

		TBlock* pre_tblock = NULL;
		VBlock* pre_vblock = NULL;
		GLOBAL::BUILD_SUPER_BLOCK = 0;

/*		XTMemAddr symb_addr = NULL;
		if(symb_addr = ee_memory_image->lookupFuncSPC("pthread_create")) {
			TBlock *tmp = ee_tcache -> requestTBlock();	
			ee_translator -> encodeLibcFuncWrapper((XTMemAddr)&(RBThreadManager::RBThreadCreate), tmp, 4);   
			ee_tcache -> submitTBlock(symb_addr,tmp,NULL);
		}
*/
		// Intercept pthread functions
		XTMemAddr symb_addr = NULL;
		for(int i=0; i<RBFUNCNUM; i++){
#ifdef RBDEBUG
	std::cout<<"Intercept pthread func:~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~!!!!!"<<std::endl
		<<"ori_agr_num: "<<RBFuncTable[i].ori_arg_num<<std::endl
		<<"ori_func_name: "<<RBFuncTable[i].ori_func_name<<std::endl
		<<"rb_func_addr: 0x"<<RBFuncTable[i].rb_func_addr<<std::endl;
#endif
			if(symb_addr = ee_memory_image->lookupFuncSPC(RBFuncTable[i].ori_func_name)) {
				TBlock *tmp = ee_tcache -> requestTBlock();	
				ee_translator -> encodeLibcFuncWrapper(RBFuncTable[i].rb_func_addr, tmp, RBFuncTable[i].ori_arg_num);   
				ee_tcache -> submitTBlock(symb_addr,tmp,NULL);
			}
		}


/*
		ee_tcache -> submitTBlock(0x40504c,tmp,NULL);	
		tmp = ee_tcache -> requestTBlock();
		ee_translator -> encodeLibcFuncWrapper((XTMemAddr)&myFree, tmp, 1);    
		ee_tcache -> submitTBlock(0x405374,tmp,NULL);
*/

#ifdef RBDEBUG
int run_count = 0;
bool in_sub = false;
#endif

		while (1) {

#ifdef DEBUG
				std::cout<<"entry address:"<<std::hex<<"0X"<<entry<<std::endl;
				XTInt32 stop = 1;
				//if( entry == 0x41738c ) while(stop);
				std::cout<<"0x3fffc014:"<<std::hex<<"0X"<<*(XTUint32*)(0x3fffc014)<<std::endl;
				std::cout<<"0x3fffc0c4:"<<(char*)(0X3fffc0c4)<<std::endl;
				std::cout<<"0x3fffc0c6:"<<(char*)(0X3fffc0c6)<<std::endl;
#endif
				TBlock *tblock = ee_tcache -> lookupTBlock(entry);
				VBlock *vblock;
				if (tblock == NULL) {
						ee_builder->getReady(entry);
						XTMemAddr next = entry;
						while( !ee_builder->isEnd() )
								next = ee_interpreter -> decode(next, ee_builder);
						vblock = ee_builder -> wrapUp(next);
#if 0 
						printf("%x\n" , entry);
						vblock->print(std::cout);
						printf("\n");
#endif
						tblock = ee_tcache -> requestTBlock();
						ee_translator -> encode(vblock, tblock);    
						ee_tcache -> submitTBlock(entry,tblock,vblock);

				}

				XTMemAddr enter = tblock->enterAddr();
#ifdef DEBUG

				if( enter == 0xa0000df7 )
						write(10, (void*)enter, 300);

				std::cout<<"enter address:"<<std::hex<<"0X"<<enter<<std::endl;
				ee_interpreter->interp_state->dump();
#endif

#ifdef LINK

				if( pre_tblock != NULL ){
						Exit exit = pre_tblock ->getExit(GLOBAL::last_exit);

						pre_vblock = ee_tcache -> lookupVBlock( pre_tblock -> getSPC() ); 
						if( exit.link_type == LinkType::DIRECT ){
								ee_trace_manager -> directLinkTo( pre_tblock , GLOBAL::last_exit , tblock );
						}else if( exit.link_type == LinkType::INDIRECT ){
								ee_trace_manager -> indirectLinkTo( pre_tblock , GLOBAL::last_exit , tblock );
						}
				}
#endif

#if 0
//#ifdef RBDEBUG
if(entry==0x400434)
{
	ee_processor_state->dump();
	vblock->print(std::cout);
	std::cout<<std::endl<<std::hex<<enter<<std::endl;
}

if(62==run_count)
{
	ee_processor_state->dump();
	vblock->print(std::cout);
	std::cout<<std::endl<<"enter(TPC):  "<<std::hex<<enter<<std::endl;
}
#endif

//#if 0
#ifdef RBDEBUG
XTPStr func_name = NULL;
if (func_name = ee_memory_image->lookupFuncName(entry))
	std::cout<<func_name<<std::endl;
#endif

				CONTEXT_SAVE
						((void (*)()) enter) ();
				CONTEXT_RESTORE

#ifdef RBDEBUG
if(in_sub){
	std::cout<<"run_count: "<<std::dec<<++run_count<<std::endl;
	std::cout<<"entry: "<<std::hex<<entry<<std::endl;
}
#endif

/*	
	if( GLOBAL::BUILD_SUPER_BLOCK ){
								GLOBAL::BUILD_SUPER_BLOCK = 0;
								entry = GLOBAL::tblock_stub -> getSPC();
								TBlock* former_tblock = ee_tcache -> lookupTBlock( entry );
								VBlock *super_vblock = Profiler::buildSuperVBlock( ee_tcache , entry ); 	
								if( super_vblock == NULL ){
										super_vblock = ee_tcache -> lookupVBlock( entry );
								}
								TBlock *super_tblock = ee_tcache -> requestTBlock();
								super_tblock -> setSuperBlock();
								ee_translator -> encode( super_vblock , super_tblock ); 	

								ee_tcache -> submitTBlock( entry , super_tblock , super_vblock );						
								ee_trace_manager -> linkSuperBlock( former_tblock , super_tblock );
								pre_tblock = NULL;	
								continue;
						}
*/

				Exit exit = GLOBAL::tblock_stub->getExit(GLOBAL::last_exit);
				if (exit.why == ExitReason::SYSCALLEXIT) {
						(*ee_syscall_handler)();
						if( GLOBAL::exit_call == XT_TRUE )
								break;
				}

				//added by RB
				if(GLOBAL::INTERCHANGE)
				{
					printf("interchange!\n");
					entry = GLOBAL::NEXT_PC;
					GLOBAL::INTERCHANGE = 0;
#ifdef RBDEBUG
//std::cout<<"new entry: "<<std::hex<<entry<<std::endl;
in_sub = true;
#endif
				}
				else
					entry = exit.next;

				pre_tblock = GLOBAL::tblock_stub;
/*				
				if( count > 100 )
				{
	XTFile fd = open("mem.dat",O_CREAT|O_RDWR, 0666);
	write(fd, &entry, sizeof(XTMemAddr) );
	ee_processor_state->dumpToFile( fd );
	ee_shadow_page->dumpMemData( fd );
	close( fd );
	count = 0;
				}
				else
					count++;
*/
		}

}

